package com.itheima.search.searchService.impl;

import com.alibaba.fastjson.JSON;
import com.itheima.pojo.GoodsInfo;
import com.itheima.search.searchService.SearchService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {
    private final RestHighLevelClient client;
    public SearchServiceImpl(@Qualifier("createRestHighLevelClient") RestHighLevelClient client) {
        this.client = client;
    }

    @Override
    public Map search(Map<String, String> searchMap) throws Exception {
        Map resultMap = new HashMap();
        //健壮性判定
        if (searchMap == null) {
            return resultMap;
        }
        String keyword = searchMap.get("keyword");
        if (StringUtils.isEmpty(keyword)) {
            return resultMap;
        }
        // 和关键字相关联的所有品牌 和 分类
        Map brandsAndCategorys = selectBrandAndCategory(keyword);
        resultMap.putAll(brandsAndCategorys);
        // 查询相关数据
        Map goods = selectGoods(searchMap);
        resultMap.putAll(goods);
        return resultMap;
    }

    private Map selectGoods(Map<String, String> searchMap) throws IOException {
        Map resultMap = new HashMap();
        // 查询相关的数据
        //1. 构建查询请求对象 SearchRequest
        SearchRequest searchRequest = new SearchRequest("goodsinfo");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();//组装条件,涉及到多个条件, 使用布尔查询
        //A. 关键字搜索
        boolQueryBuilder.must(QueryBuilders.matchQuery("name", searchMap.get("keyword")));
        //B. 品牌搜索
        if (!StringUtils.isEmpty(searchMap.get("brand"))) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
        }
        //C. 分类搜索
        if (!StringUtils.isEmpty(searchMap.get("category"))) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", searchMap.get("category")));
        }
        //E. 价格区间搜索
        if (!StringUtils.isEmpty(searchMap.get("price"))) {
            String[] prices = searchMap.get("price").split("-");// 0-500 500-1000
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(Integer.parseInt(prices[0])));
            if (prices.length == 2) {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(prices[1])));
            }
        }
        sourceBuilder.query(boolQueryBuilder);
        //排序
        if (!StringUtils.isEmpty(searchMap.get("sortField")) && !StringUtils.isEmpty(searchMap.get("sortRule"))) {
            if ("ASC".equals(searchMap.get("sortRule"))) {
                sourceBuilder.sort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.ASC));//升序
            } else {
                sourceBuilder.sort(SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.DESC));//降序
            }
        }
        // 添加分页信息
        Integer pageNum = Integer.parseInt(String.valueOf(searchMap.get("pageNum"))); //当前页
        Integer pageSize = Integer.parseInt(String.valueOf(searchMap.get("pageSize"))); //每页显示多少条
        if (null == pageNum || pageNum < 1) {
            pageNum = 1;
        }
        if (null == pageSize || pageSize < 1) {
            pageSize = 10;
        }
        sourceBuilder.from((pageNum - 1) * pageSize);
        sourceBuilder.size(pageSize);
        //设置高亮域以及高亮的样式
        sourceBuilder.highlighter(new HighlightBuilder().field("name").preTags("<span style='color:red'>").postTags("</span>"));
        searchRequest.source(sourceBuilder);
        //3. 查询,获取查询结果
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        //4. 解析结果
        SearchHits searchHits = searchResponse.getHits();
        long count = searchHits.getTotalHits().value;

        List<GoodsInfo> goodsInfoList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            //获取原始结果
            String _source = hit.getSourceAsString();
            GoodsInfo goodsInfo = JSON.parseObject(_source, GoodsInfo.class);
            //获取高亮结果, 并解析
            Map<String, HighlightField> highlightFieldMap = hit.getHighlightFields();
            if (highlightFieldMap != null) {
                HighlightField highlightField = highlightFieldMap.get("name");
                if (highlightField != null) {
                    Text[] fragments = highlightField.getFragments();
                    StringBuilder hl_name = new StringBuilder();
                    if (fragments != null) {
                        for (Text fragment : fragments) {
                            hl_name.append(fragment.toString());
                        }
                        goodsInfo.setName(hl_name.toString());
                    }
                }
            }
            goodsInfoList.add(goodsInfo);
        }
        resultMap.put("pageNum", pageNum);
        resultMap.put("pageSize", pageSize);
        resultMap.put("total", count);
        resultMap.put("goodList", goodsInfoList);
        if (count % pageSize == 0) {
            resultMap.put("pages", count / pageSize);
        } else {
            resultMap.put("pages", count / pageSize + 1);
        }
        return resultMap;
    }

    // 先聚合查询 和此关键字相关的  品牌 与 分类
    private Map selectBrandAndCategory(String keyword) throws IOException {
        Map resultMap = new HashMap();
        SearchRequest searchRequest = new SearchRequest("goodsinfo");
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.matchQuery("name", keyword));
        builder.size(0);
        //按照品牌进行分组(聚合)查询  -- 设置size , 不设置, 默认查询十个
        String _brand = "brand";
        builder.aggregation(AggregationBuilders.terms(_brand).field("brandName").size(Integer.MAX_VALUE));
        //分类聚合
        String _category = "category";
        builder.aggregation(AggregationBuilders.terms(_category).field("categoryName").size(Integer.MAX_VALUE));
        searchRequest.source(builder);
        //3. 查询,获取查询结果
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//解析分组结果
        Map<String, List<String>> aggMap = new HashMap<String, List<String>>();
        Aggregations aggregations = searchResponse.getAggregations();
        for (Aggregation aggregation : aggregations) {
            ParsedStringTerms st = (ParsedStringTerms) aggregation;
            List<String> results = new ArrayList<String>();
            List<? extends Terms.Bucket> buckets = st.getBuckets();
            if (buckets != null) {
                for (Terms.Bucket bucket : buckets) {
                    results.add(bucket.getKeyAsString());
                }
            }
            aggMap.put(st.getName(), results);
        }
        //组装需要返回的结果
        resultMap.put("brandList", aggMap.get("brand"));
        resultMap.put("categoryList", aggMap.get("category"));
        return resultMap;
    }
}
